package wcg;

import ikelib.byteUtil;
import ikelib.wintime;

import java.io.BufferedInputStream;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import java.util.TreeSet;


public class K_NET_CNT {
	
	public byte[] DataRow = null;
	public int DisplayLevel = 0;									// 0:全く表示しない、3:全部表示する
	public List<Integer> SMIndexTable = new ArrayList<Integer>();
	public wintime StartTime = null;								// データの時刻

	public TreeSet<Integer> ChannelID = null;						// channel ID
	public HashMap<Integer,Integer> SampleRate = null; 				// サンプリングレート
	public HashMap<Integer,int []> Data  = null; 					// データ
	
	private wintime CurrentTime = null;
	private LinkedList<Integer> Temp2 = null;
	
	private HashMap<Integer,Integer> Logical2PhysicalChannel = null;
	private HashMap<Integer,LinkedList<int[]>> DataTemp = null;
	
	
	K_NET_CNT(){
	};
	K_NET_CNT(String file){
		int res = load(file);
		if(res != 0) return;
	};
	/**
	 * byte[] から構築
	 * @param c
	 */
	K_NET_CNT(byte[] c){
		load(c);
		
	}
	public int check(){
		if(DataRow == null){
			return -1;
		}
		return purse();
	}
	// バッファをパースする
	public int purse(){
		try {
			Temp2 = new LinkedList<Integer>();
			ChannelID = new TreeSet<Integer>(); // channel ID
			SampleRate = new HashMap<Integer,Integer>(); // サンプリングレート
			Data  = new HashMap<Integer,int []>(); // データ
			Logical2PhysicalChannel = new HashMap<Integer,Integer>(); // 論理チャンネル→物理チャンネル変換テーブル
			
			int index = 0;
			int len = purse_header(index);
			if(len <= 0) return -2;
			index += len;
			while(index<DataRow.length){
				int id = byteUtil.convShort(DataRow[index]);
				int leng=0;
				switch(id){
				case 0x5: // 波形パケット
					leng = purse_wave5(index);
					break;
				case 0x21: // 波形情報 （たぶん）
					leng = purse_waveinfo(index);
					break;
				case 0x31: // CALパケット
					leng = purse_cal(index);
					break;
				default:
					leng = purse_unknown(index);
				}
				if(leng<=0){
					return -3;
				}
				index += leng;
			}
			// 温度情報をデータに加える
			int idtemp = makeTemp1ChannelId();
			ChannelID.add(idtemp);
			len = Temp2.size();
			SampleRate.put(idtemp,1);	// 1spsを仮定している
			int[] tdata = new int[len];
			Iterator<Integer> ite = Temp2.iterator();
			int ccc=0;
			while(ite.hasNext()){
				Integer d = ite.next();
				tdata[ccc++]=d;
			}
			Data.put(idtemp, tdata);
			
			// データを作る
			Iterator<Integer> ite2 = ChannelID.iterator();
			while(ite2.hasNext()){
				int id = ite2.next();
				int smp = SampleRate.get(id);
				if(smp <= 0){
					continue;
				}
				LinkedList<int[]> dlist = DataTemp.get(id);
				
				if(dlist==null){
					continue;
					//dlist = new LinkedList<int[]>();
				}
				Iterator<int[]> ite3= dlist.iterator();
				int leng=0;
				while(ite3.hasNext()){
					int[] d = ite3.next();
					leng += d.length;
				}
				int[] ddd = new int[leng];
				Iterator<int[]> ite4 = dlist.iterator();
				int idx2=0;
				while(ite4.hasNext()){
					int[] d = ite4.next();
					int lll = d.length;
					System.arraycopy(d, 0, ddd,idx2 , lll);
					idx2+=lll;
				}
				ChannelID.add(id);
				SampleRate.put(id,smp);	
				Data.put(id, ddd);
			}
		} catch (ArrayIndexOutOfBoundsException e) {
			System.err.println(e);
			System.err.println("Error file format detected !");
			return -10;
		}
		
		return 0;
	}
	/*
	 * 波形
	 */
	private int purse_wave5(int index){
		prt(1,String.format("%04x :  ** wave5(0x5) ***",index),null);
		index++;
		int len = byteUtil.convShort(DataRow[index++]) * 0x100 + byteUtil.convShort(DataRow[index++]);
		int lastindex = index + len -3;
		
		short fmtver = byteUtil.convShort(DataRow[index++]);
		prt(2," Format version",String.format("%02x",fmtver));
		if(fmtver != 2){
			prt(1,"Not supported version",null);
			return -1;
		}
		int chnum = byteUtil.convShort(DataRow[index++]);
		prt(2," Channel number",String.format("%d",chnum));
		
		// 時刻
		wintime wt = new wintime(DataRow,++index);
		index+=6;
		int subsec = wintime.bcd2int(DataRow[index++]);	// 00-99  
		wt.inc_msec(10*subsec);
		prt(2," Time",wt.fmtmSec());
		CurrentTime = new wintime(wt); //現在時刻
		
		
		// フレーム時間　1:0.1sec 10:1sec
		int flame = byteUtil.convShort(DataRow[index++]);
		prt(2," Channel number",String.format("%d",flame));
		// 強震指標
		Iterator<Integer>  ite = SMIndexTable.iterator();
		while(ite.hasNext()){
			int smindex;
			Integer idx = ite.next();
			if((idx & 0x1) !=0){// リアルタイム震度
				smindex = byteUtil.convShort(DataRow[index++]) * 0x100 + byteUtil.convShort(DataRow[index++]);
				prt(2,"  Real Time Intensity",String.format("%d", smindex));
			}
			if((idx & (0x1<<1))!=0){// PGA
				smindex = byteUtil.convShort(DataRow[index++]) * 0x100 + byteUtil.convShort(DataRow[index++]);
				prt(2,"  PGA",String.format("%d", smindex));
			}
			if((idx & (0x1<<2)) !=0){// PGV
				smindex = byteUtil.convShort(DataRow[index++]) * 0x100 + byteUtil.convShort(DataRow[index++]);
				prt(2,"  PGV",String.format("%d", smindex));
			}
			if((idx & (0x1<<3)) !=0){// PGD
				smindex = byteUtil.convShort(DataRow[index++]) * 0x100 + byteUtil.convShort(DataRow[index++]);
				prt(2,"  PGD",String.format("%d", smindex));
			}
			if((idx & (0x1<<4)) !=0){// 応答スペクトル 0.125Hz
				smindex = byteUtil.convShort(DataRow[index++]) * 0x100 + byteUtil.convShort(DataRow[index++]);
				prt(2,"  Resp. Spectrum(0.125Hz)",String.format("%d", smindex));
			}
			if((idx & (0x1<<5)) !=0){// 応答スペクトル 0.25Hz
				smindex = byteUtil.convShort(DataRow[index++]) * 0x100 + byteUtil.convShort(DataRow[index++]);
				prt(2,"  Resp. Spectrum(0.25Hz)",String.format("%d", smindex));
			}
			if((idx & (0x1<<6)) !=0){// 応答スペクトル 0.5Hz
				smindex = byteUtil.convShort(DataRow[index++]) * 0x100 + byteUtil.convShort(DataRow[index++]);
				prt(2,"  Resp. Spectrum(0.5Hz)",String.format("%d", smindex));
			}
			if((idx & (0x1<<7)) !=0){// 応答スペクトル 1Hz
				smindex = byteUtil.convShort(DataRow[index++]) * 0x100 + byteUtil.convShort(DataRow[index++]);
				prt(2,"  Resp. Spectrum(1Hz)",String.format("%d", smindex));
			}
			if((idx & (0x1<<8)) !=0){// 応答スペクトル 2Hz
				smindex = byteUtil.convShort(DataRow[index++]) * 0x100 + byteUtil.convShort(DataRow[index++]);
				prt(2,"  Resp. Spectrum(2Hz)",String.format("%d", smindex));
			}
			if((idx & (0x1<<9)) !=0){// 応答スペクトル 4Hz
				smindex = byteUtil.convShort(DataRow[index++]) * 0x100 + byteUtil.convShort(DataRow[index++]);
				prt(2,"  Resp. Spectrum(4Hz)",String.format("%d", smindex));
			}
		}
		while(lastindex > index){	// チャンネルごと
			// 論理チャネル
			int logicalChannel = byteUtil.convShort(DataRow[index++]);
			prt(2,"  Logical channel",String.format("%d",logicalChannel));
		
			// データ数
			int datanum = byteUtil.convShort(DataRow[index++])*0x100 + byteUtil.convShort(DataRow[index++]);
			prt(2,"   DataRow # ",String.format("%d",datanum));
			
			// 圧縮後のデータbit
			int databit = byteUtil.convShort(DataRow[index++]);
			prt(2,"   DataRow bit",String.format("%d",databit));
	
			// 圧縮方法
			int compress = byteUtil.convShort(DataRow[index++]);
			prt(2,"   Compress type",String.format("%02xH",compress));
			
			int[] data = null;
			switch(compress){
			case 0x11:
				data = decode0x11(index,datanum,databit);
				index += getSize(datanum,databit,compress);
				if(data!=null){
					int id = Logical2PhysicalChannel.get(logicalChannel);
					if(DataTemp==null){
						DataTemp = new HashMap<Integer,LinkedList<int[]>>();
					}
					LinkedList<int[]> dlist = DataTemp.get(id);
					if(dlist==null) {
						dlist = new LinkedList<int[]>();
					}
					dlist.add(data);
					DataTemp.put(id, dlist);
				}
				break;
			default:
				System.out.println(String.format("*** Decode type %02xH not supported ",compress));
				return -1;
			}
		}
		return len;
	}
	/*
	 * 
	 */
	private int getSize(int datanum,int databit,int compresstype){
		int ret = 0;
		switch(compresstype){
		case 0x11:
			switch(databit){
			case 4:
				ret=3+(datanum-1+1)/2;
				break;
			case 8:
				ret=3+(datanum-1);
				break;
			case 12:
				ret=3+((datanum-1)*3+1)/2;
				break;
			case 16:
				ret=3+(datanum-1)*2;
				break;
			}
			break;
		}
		return ret;
	}
	
	private int convAutoNegative(int in,int bit){
		int mask = (1 << (bit-1));
		if((in & mask) == 0) return in;
		int or = 0xffffffff << bit;
		return in | or;
	}
	/*
	 *  0x11形式をデコードする
	 */
	private int[] decode0x11(int index,int datanum,int databit){
		int[] ret = new int[datanum];
		int ccc = 0;
		int tmp = (int)byteUtil.convShort(DataRow[index++]) * 0x10000  + byteUtil.convShort(DataRow[index++]) * 0x100 + byteUtil.convShort(DataRow[index++]);
		ret[ccc++] = convAutoNegative(tmp,24);
		switch(databit){
			case 4:
				for(int j=1;;){
					int udata = byteUtil.convShort(DataRow[index++]);
					int udata1 = (udata>>4) & 0xf;
					udata1 = convAutoNegative(udata1,4);
					ret[ccc++] = ret[ccc-2] + udata1; 
					j++; 
					if(j>=datanum) break;
					udata1 = (udata>>0) & 0xf;
					udata1 = convAutoNegative(udata1,4);
					ret[ccc++] = ret[ccc-2] + udata1;
					j++;
					if(j>=datanum) break;
				}
				break;
			case 8:
				for(int j=1;;){
					int udata = byteUtil.convShort(DataRow[index++]);
					int udata1 = convAutoNegative(udata,8);
					ret[ccc++] = ret[ccc-2] + udata1; 
					j++; 
					if(j>=datanum) break;
				}
				break;
			case 12:
				for(int j=1;;){
					int udata = 0;
					if(j % 2 == 1){
						udata = byteUtil.convShort(DataRow[index++]) * 0x10 + ((byteUtil.convShort(DataRow[index])>>4) &0xf) ;
					}
					else{
						udata = (byteUtil.convShort(DataRow[index++]) & 0xf)*0x100 + (byteUtil.convShort(DataRow[index++]));
					}
					int udata1 = convAutoNegative(udata,12);
					ret[ccc++] = ret[ccc-2] + udata1; 
					j++; 
					if(j>=datanum) break;
				}
				break;
			case 16:
				for(int j=1;;){
					int udata = byteUtil.convShort(DataRow[index++])*0x100 + byteUtil.convShort(DataRow[index++]);
					int udata1 = convAutoNegative(udata,16);
					ret[ccc++] = ret[ccc-2] + udata1; 
					j++; 
					if(j>=datanum) break;
				}
				break;
			default:
				ret = null;
				break;
		}
		return ret;
	}
	
	private int makeTemp1ChannelId(){
		return 0xfffe;
	}
	/*
	 * CAL
	 */
	private int purse_cal(int index){
		prt(1,String.format("%04x :  ** CAL(0x31) ***",index),null);
		index++;
		int len = byteUtil.convShort(DataRow[index++]) * 0x100 + byteUtil.convShort(DataRow[index++]);
		short fmtver = byteUtil.convShort(DataRow[index++]);
		prt(2," Format version",String.format("%02x",fmtver));
		if(fmtver != 2){
			prt(1,"Not supported version",null);
			return -1;
		}
		short cal = byteUtil.convShort(DataRow[index++]);
		prt(2," CAL",String.format("%02x",cal));
		return len;
	}
	/*
	 * 波形情報　
	 */
	private int purse_waveinfo(int index){
		prt(1,String.format("%04x :  ** WaveInfo(0x21) ***",index),null);
		index++;
		int len = byteUtil.convShort(DataRow[index++]) * 0x100 + byteUtil.convShort(DataRow[index++]);
		short fmtver = byteUtil.convShort(DataRow[index++]);
		prt(2," Format version",String.format("%02x",fmtver));
		if(fmtver != 2){
			prt(1,"Not supported version",null);
			return -1;
		}
		index++;// CAL
		index++; // UTC Offset
		index++; // leap second info
		// GPS 最終時刻校正
		wintime wt = new wintime(DataRow,++index);
		index+=6;
		int subsec = wintime.bcd2int(DataRow[index++]);	// 00-99  
		wt.inc_msec(10*subsec);
		prt(2," GPS Time comp.",wt.fmtmSec());
		// 時刻誤差
		int timediff = byteUtil.convShort(DataRow[index++])*0x100 +  byteUtil.convShort(DataRow[index++]);
		prt(2," Time difference(sec)",String.format("%d",timediff));

		// 時刻誤差
		int timediffms = byteUtil.convShort(DataRow[index++])*0x100 +  byteUtil.convShort(DataRow[index++]);
		prt(2," Time difference(msec)",String.format("%d",timediffms));

		// 温度測定 時刻
		wt = new wintime(DataRow,++index);
		index+=6;
		subsec = wintime.bcd2int(DataRow[index++]);	// 00-99  
		wt.inc_msec(10*subsec);
		prt(2," Temp. measure",wt.fmtmSec());
		
		// 温度
		int temp1 = byteUtil.convShort(DataRow[index++])*0x100 +  byteUtil.convShort(DataRow[index++]);
		temp1 = convAutoNegative(temp1,16);
		prt(2," Temparature1",String.format("%.2f",(float)temp1/100));
		
		// 温度2
		int temp2 = byteUtil.convShort(DataRow[index++])*0x100 +  byteUtil.convShort(DataRow[index++]);
		temp2 = convAutoNegative(temp2,16);
		prt(2," Temparature2",String.format("%.2f",(float)temp2/100));
		Temp2.add(temp2);

		// 電源状態
		prt(2," Power state ",String.format("%02x %02x", DataRow[index++],DataRow[index++]));
		
		// ch電源状態
		prt(2," Channel state ",String.format("%02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x ", 
				DataRow[index++],DataRow[index++],DataRow[index++],DataRow[index++],DataRow[index++],DataRow[index++],
				DataRow[index++],DataRow[index++],DataRow[index++],DataRow[index++],DataRow[index++],DataRow[index++]
				));

		return len;
	}
	/*
	 * よくわからないデータを表示する
	 */
	private int purse_unknown(int index){
		prt(1,String.format("%04x :  ** Unknown ID(0x02x) ***",index,DataRow[index++]),null);
		int len = byteUtil.convShort(DataRow[index++]) * 0x100 + byteUtil.convShort(DataRow[index++]);
		int datalen = len -3;
		if(datalen>0){
			if(datalen>10) datalen=10;
			String mes = new String();
			for(int i=0;i<datalen;i++){
				mes += String.format("%02x ",DataRow[index+i]);
			}
			prt(2," DataRow",mes);
		}
		return len;
	}
	/*
	 * ヘッダーを解析する
	 */
	private int purse_header(int index){
		if(DataRow[0] != -125 || DataRow[1] != 0x06) return -1;
		index  += 2;
		int len = byteUtil.convShort(DataRow[index]) * 0x100 + byteUtil.convShort(DataRow[index+1]);
		index += 2;
		prt(0,String.format("%04x : *** Header ***",index),null);
		
		short fmtver = byteUtil.convShort(DataRow[index++]);
		prt(0," Format version",String.format("%02x",fmtver));
		if(fmtver != 2){
			prt(1,"Not supported version",null);
			return -1;
		}
		
		int siteID = byteUtil.convShort(DataRow[index++])*0x100 +  byteUtil.convShort(DataRow[index++]);
		prt(0," Site ID",String.format("%04x",siteID));
		index++;index++;// session ID
		index++;// 機器番号
		index++;// トリガ機器
		int maxchannel = byteUtil.convShort(DataRow[index++]); // 最大チャネル数
		prt(0," Max channel",String.format("%d",maxchannel));
		
		index++;// 組織ID
		index++;// 網内組織ID
		index++;index++;// 組織内チャンネル番号
		short timeadjustmode = byteUtil.convShort(DataRow[index++]);
		prt(0," Time Sync mode",String.format("%02x",timeadjustmode));
		
		wintime wt = new wintime(DataRow,++index);
		index+=6;
		int subsec = wintime.bcd2int(DataRow[index++]);	// 00-99  
		wt.inc_msec(10*subsec);
		prt(0," Time",wt.fmtmSec());
		StartTime = new wintime(wt);// スタートタイム
	
		
		int strongMotiionIndexNum = byteUtil.convShort(DataRow[index++]);	// 強震指標群の個数 =4  
		
		for(int j=0;j<strongMotiionIndexNum;j++){
			byte index1,index2;
			prt(1,String.format(" Stg. Motion Idx. %d",j+1),String.format("%02x %02x %02x %02x %02x %02x",DataRow[index++],DataRow[index++],DataRow[index++],DataRow[index++],(byte)(index1=DataRow[index++]),(byte)(index2=DataRow[index++])));
			int inx = byteUtil.convShort(index1) * 0x100 + byteUtil.convShort(index2);
			SMIndexTable.add(inx);
		}
		int  usechannel=0;
		for(int loop = 1;maxchannel>= loop;loop++){
			prt(1,String.format(" *** Channel %d ***",loop),null);
			
			int logicalhannel = byteUtil.convShort(DataRow[index++]);	// チャンネル数
			prt(1," Logical Channel", String.format("%d",logicalhannel));
			int inchannel = byteUtil.convShort(DataRow[index++]);	// チャンネル数
			prt(1," Input Channel",String.format("%d",inchannel));
			int channelID = byteUtil.convShort(DataRow[index++])*0x100 +  byteUtil.convShort(DataRow[index++]);
			prt(1," Channel ID",String.format("%04x",channelID));
			
			
			int valid = byteUtil.convShort(DataRow[index++]);
			if(valid>0) usechannel++;
			prt(1," Valid",String.format("%d",valid));
			int status = byteUtil.convShort(DataRow[index++]);
			prt(1," Status",String.format("%d",status));
			int scalenum = byteUtil.convShort(DataRow[index++])*0x100 +  byteUtil.convShort(DataRow[index++]);
			prt(1," Scale Numerator",String.format("%d",scalenum));
			int scaleden = byteUtil.convShort(DataRow[index++])*0x1000000 +  byteUtil.convShort(DataRow[index++])*0x10000 + byteUtil.convShort(DataRow[index++])*0x100 +  byteUtil.convShort(DataRow[index++]);
			prt(1," Scale Denominator",String.format("%d",scaleden));
			
			prt(1," Gain,Unit... ",String.format("%02x %02x %02x %02x %02x %02x",byteUtil.convShort(DataRow[index++]),byteUtil.convShort(DataRow[index++]),byteUtil.convShort(DataRow[index++]),byteUtil.convShort(DataRow[index++]),byteUtil.convShort(DataRow[index++]),byteUtil.convShort(DataRow[index++])));
			
			int cliprange = byteUtil.convShort(DataRow[index++])*0x1000000 +  byteUtil.convShort(DataRow[index++])*0x10000 + byteUtil.convShort(DataRow[index++])*0x100 +  byteUtil.convShort(DataRow[index++]);
			prt(1," Clip range",String.format("%d",cliprange));
			
			int samplerate = byteUtil.convShort(DataRow[index++])*0x100 +  byteUtil.convShort(DataRow[index++]);
			prt(1," Sample rate",String.format("%d",samplerate));
			if(samplerate>0){
				ChannelID.add(channelID);
				Logical2PhysicalChannel.put(logicalhannel, channelID);
				SampleRate.put(channelID,samplerate);
			}
			
		}
		return len;
	}
	// STDOUTに出力
	private void prt(int dsplevel , String str1 , String str2){
		if(DisplayLevel>dsplevel){
			String mes = str1;
			for(int i=mes.length();i<20;i++){
				mes += " ";
			}
			if(str2!=null){
				mes += "= ";
				mes += str2;
			}
			System.out.println(mes);
		}
	}
	/*
	 * ファイルからロードする
	 */
	private int load(String file){
		int result = 0;
		BufferedInputStream fis = null;
		int count=0;
		try {
			File f = new File(file);
			int total = (int)f.length();
			DataRow = new byte[total]; 
			fis = new BufferedInputStream(new FileInputStream(f));
            int avail;
            // 読み込み可能なバイト数づつ読み込む
            while ((avail = fis.available()) > 0) {
                byte[] bytes = new byte[avail];
                fis.read(bytes);
                for(int j=0;j<avail;j++){
                	DataRow[count++] = bytes[j];
                }
            }
		} catch (IOException e) {
			e.printStackTrace();
			DataRow = null;
			result =  -10; //ファイルが無い、アクセスできない
		} finally {
			try {
				if (fis != null) {
                    // ストリームは必ず finally で close します。
                    fis.close();
                }
            } catch (IOException e) {
            }			
		}
		return result;
	}
	/*
	 * バッファからロードする
	 */
	private int load(byte in[]){
		DataRow = new byte[in.length];
		System.arraycopy(in, 0, DataRow, 0, in.length);
		return 0;
	}
}
